package solver

import (
	"fmt"
	"log"
	"strings"
)

func (g *Grid) Skycraper() (used bool) {
	for cand := 0; cand < 9; cand++ {
		if g.skycraperCand(cand, g.rows[:]) {
			used = true
			return
		}
	}
	for cand := 0; cand < 9; cand++ {
		if g.skycraperCand(cand, g.cols[:]) {
			used = true
			return
		}
	}
	return
}

func (g *Grid) skycraperCand(cand int, lines []House) (used bool) {
	chains := getStrongChainWithCand(lines, cand)
	for i, chain1 := range chains {
		for _, chain2 := range chains[i+1:] {
			ok, cells, wings := verifySkycraper(chain1, chain2)
			if !ok {
				continue
			}
			if g.clearupTurbotFish(cand, cells, wings) {
				used = true
				logTurbotFish(toValue(cand), cells, wings, "Skycraper")
			}
		}
	}
	return
}

func (g *Grid) TwoStringKite() (used bool) {
	for cand := 0; cand < 9; cand++ {
		if g.twoStringKiteCand(cand) {
			used = true
			return
		}
	}
	return
}

func (g *Grid) twoStringKiteCand(cand int) (used bool) {
	chains1 := getStrongChainWithCand(g.rows[:], cand)
	chains2 := getStrongChainWithCand(g.cols[:], cand)
	for _, chain1 := range chains1 {
		for _, chain2 := range chains2 {
			ok, cells, wings := verifyTwoStringKite(chain1, chain2)
			if !ok {
				continue
			}
			if g.clearupTurbotFish(cand, cells, wings) {
				used = true
				logTurbotFish(toValue(cand), cells, wings, "Two String Kite")
			}
		}
	}
	return
}
func verifyTwoStringKite(chain1, chain2 []*Cell) (ok bool, cells, wings []*Cell) {
	for i, cell1 := range chain1 {
		for j, cell2 := range chain2 {
			if cell1 == cell2 || cell1.boxNum != cell2.boxNum {
				continue
			}
			ok = true
			cells = []*Cell{cell1, cell2}
			wings = []*Cell{chain1[1-i], chain2[1-j]}
			return
		}
	}

	return
}

func (g *Grid) TurbotFish() (used bool) {
	for cand := 0; cand < 9; cand++ {
		if g.turbotFishCand(cand) {
			used = true
			return
		}
	}
	return
}

func (g *Grid) turbotFishCand(cand int) (used bool) {
	lineChains := getStrongChainWithCand(g.rows[:], cand)
	lineChains = append(lineChains, getStrongChainWithCand(g.cols[:], cand)...)
	boxChains := getStrongChainWithCand(g.boxes[:], cand)

	for _, lineChain := range lineChains {
		for _, boxChain := range boxChains {
			ok, cells, wings := verifyTurbotFish(lineChain, boxChain)
			if !ok {
				continue
			}
			if g.clearupTurbotFish(cand, cells, wings) {
				used = true
				logTurbotFish(toValue(cand), cells, wings, "Turbot Fish")
			}
		}
	}
	return
}

func verifyTurbotFish(lineChain, boxChain []*Cell) (ok bool, cells, wings []*Cell) {
	if boxChain[0].rowNum == boxChain[1].rowNum || boxChain[0].colNum == boxChain[1].colNum {
		return
	}
	for i, cell1 := range lineChain {
		for j, cell2 := range boxChain {
			if cell1 == cell2 {
				continue
			}
			if cell1.rowNum != cell2.rowNum && cell1.colNum != cell2.colNum {
				continue
			}
			ok = true
			cells = []*Cell{cell1, cell2}
			wings = []*Cell{lineChain[1-i], boxChain[1-j]}
			return
		}
	}

	return
}
func getStrongChainWithCand(houses []House, cand int) (chains [][]*Cell) {
	for _, house := range houses {
		count := 0
		cells := []*Cell{}
		for _, cell := range house.cells {
			if cell.cands.Has(cand) {
				count++
				if count > 2 {
					break
				}
				cells = append(cells, cell)
			}
		}
		if count == 2 {
			chains = append(chains, cells)
		}
	}
	return
}

func verifySkycraper(chain1, chain2 []*Cell) (ok bool, cells, wings []*Cell) {
	isRow := chain1[0].rowNum == chain2[0].rowNum
	for i, cell1 := range chain1 {
		for j, cell2 := range chain2 {
			if (isRow && cell1.colNum == cell2.colNum && chain1[1-i].colNum != chain2[1-j].colNum) || (!isRow && cell1.rowNum == cell2.rowNum && chain1[1-i].rowNum != chain2[1-j].rowNum) {
				ok = true
				cells = []*Cell{cell1, cell2}
				wings = []*Cell{chain1[1-i], chain2[1-j]}
				return
			}
		}
	}
	return
}

func (g *Grid) clearupTurbotFish(cand int, cells, wings []*Cell) (used bool) {
	shares := shareCells(g.seenCells(wings[0]), g.seenCells(wings[1]))

	for _, cell := range shares {
		if cell.isMemberOf(cells) || cell.isMemberOf(wings) {
			continue
		}
		if cell.removeCand(cand) {
			used = true
		}
	}

	return
}

func logTurbotFish(value int, cells, wings []*Cell, sname string) {
	var b strings.Builder
	fmt.Fprintf(&b, "pr%vc%v pr%vc%v ", cells[0].rowNum, cells[0].colNum, cells[1].rowNum, cells[1].colNum)
	fmt.Fprintf(&b, "wr%vc%v wr%vc%v ", wings[0].rowNum, wings[0].colNum, wings[1].rowNum, wings[1].colNum)
	fmt.Fprintf(&b, "%v %s", value, sname)
	log.Print(b.String())
}
